package com.license.logic.clonalselection;

import java.io.File;
import java.io.IOException;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.w3c.dom.DOMException;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import com.license.common.util.Constants;
import com.license.logic.behaviorlog.RiskCalculation;
import com.license.logic.behaviorlog.RiskFactor;
import com.license.logic.dao.ActivityDao;
import com.license.logic.dao.NutrionDAO;

public class ClonalSelectionOptimization {
	
	private int antibodyNumber;
	private int cloneIteration;
	
	private ActivityDao activityDao = new ActivityDao();
	private NutrionDAO nutrionDAO = new NutrionDAO();

	private static Map<String, Double> difference;
	private RiskFactor riskFactor;
	private Operation operation;
	
	private int paretoOptimalMethod;
	private int diateNrGenerated;
	private int betaValue;
	private int clonedAntibodyNr;
	
	public ClonalSelectionOptimization() {
		
	}
	
	public ClonalSelectionOptimization(String path, int antibodyNumber, int cloneIteration, int paretoOptimalMethod,
			int diateNrGenerated, int betaVlue, int clonedAntibodyNr) throws IOException, ParserConfigurationException, SAXException {
		 
		this.antibodyNumber = antibodyNumber;
		this.cloneIteration = cloneIteration;
		
		this.paretoOptimalMethod = paretoOptimalMethod;
		this.diateNrGenerated = diateNrGenerated;
		this.betaValue = betaVlue;
		this.clonedAntibodyNr = clonedAntibodyNr;
		
		this.operation = new Operation();
		RiskCalculation riskCalculation = new RiskCalculation();
		this.riskFactor = riskCalculation.getRiskFactorsFromLog(path);
		this.operation.initializeDocumentBuilder(path);
		this.operation.setExceptedFoods(nutrionDAO.getExceptedFoods());
		this.operation.setPreferencedActivities(this.operation.getPreferencedActivitiesFromPath(activityDao.getAllActivities()));
		this.operation.setPreferencedFoods( this.operation.getPreferencedFoodsFromPath(nutrionDAO.getAllFoods()));
		this.operation.setAllFoods(nutrionDAO.getAllFoods());

		try {
			this.difference = this.operation.preprocessing(riskFactor);
		} catch (DOMException | ParserConfigurationException | SAXException
				| IOException | ParseException e) {
			e.printStackTrace();
		}

		double regularyDailyKalory = this.riskFactor.getAdditional()
				.getNutriotionKalory()
				- this.riskFactor.getAdditional().getAcitivityKalory();
		this.operation.setDesireKalory(this.operation.findKaloryNumber(
				difference.get("weight"), 1, regularyDailyKalory));
		this.operation.setDesireSbp(this.riskFactor.getSbp() + this.difference.get("sbp"));  // este sbp-ul la care vrem sa ajungem
		
//		for (String str : this.operation.getExceptedFoods().keySet()){
//			System.out.println("################### " + str + " ###################");
//			for (String st : this.operation.getExceptedFoods().get(str)){
//				System.out.println("food : " + st);
//			}
//		}
	}
	
	public List<Antibody> initializeAntibodies(int anticorpNr) {
		List<Antibody> resultAntibodies = new ArrayList<Antibody>();

		for (int i = 0; i < anticorpNr; i++) {

			Antibody antibody = new Antibody();
			
			antibody.setActivities(this.operation.generateRandomActivities(this.riskFactor.getWeight()));
			antibody.setAlimentation(this.operation.generateRandomAlimentation(Constants.XML_CONSTRAINT_MENTHOD));
			antibody.setWeight(this.riskFactor.getWeight());

			resultAntibodies.add(antibody);
		}

		return resultAntibodies;

	}
	
	public void initiateClonalSelection(String filePath, String automaticType){
		File fXmlFile = new File(filePath);
		DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
		DocumentBuilder dBuilder;
		try {
			dBuilder = dbFactory.newDocumentBuilder();
			Document doc = dBuilder.parse(fXmlFile);
			doc.getDocumentElement().normalize();
			NodeList cso = doc.getElementsByTagName(automaticType).item(0).getChildNodes();
			cso.item(1).getNodeName();
			paretoOptimalMethod = Integer.parseInt(cso.item(1).getTextContent());
			diateNrGenerated = Integer.parseInt(cso.item(3).getTextContent());
			betaValue = Integer.parseInt(cso.item(5).getTextContent());
			clonedAntibodyNr = Integer.parseInt(cso.item(7).getTextContent());
			
		} catch (Exception e){
			
		}
	}
	
	public List<Antibody> clonalSelectionOptimization () {
		
		List<Antibody> bestAntibodies = new ArrayList<Antibody>();
		
		for (int i = 0 ; i < diateNrGenerated; i++){
		
			List<Antibody> initialAntibodies = this.initializeAntibodies(antibodyNumber);
			
			Antibody bestAntibody = null;
			// we will have 7 antibodies, one for every day of week
			for (int iteration = 1; iteration<= cloneIteration;iteration ++) {
				// select first N antibodies
				List<Antibody> selectedAntibodies = this.operation.select(initialAntibodies, antibodyNumber);
			 	// clone them according to index values which reflects how good is his fitness value (first index is the best, last position is the worst)
				Map<Antibody, List<Antibody>> amtibodyMap = this.operation.cloneSelectedAntibodies(selectedAntibodies, betaValue, antibodyNumber);
			 	// permute the food item between them
				this.operation.hypermut(amtibodyMap,selectedAntibodies, betaValue, antibodyNumber);
				// get a list of all cloned antibodies inserted in order of fitness value
				List<Antibody> antibodyClones = this.operation.getOrderedListFromMap(amtibodyMap, antibodyNumber, this.riskFactor.getSbp(),paretoOptimalMethod);
				// select first n of them
				antibodyClones = this.operation.select(antibodyClones, antibodyNumber);
				// insert these selected antibody clones into selectedAntibodies
				this.operation.insertAllAntibodiesInOrder(selectedAntibodies, antibodyClones, antibodyNumber, this.riskFactor.getSbp(),paretoOptimalMethod);
				// generated m clones randomly
				List<Antibody> randomAntibodies = this.operation.generateRandomAntibodies(clonedAntibodyNr, this.riskFactor.getWeight(), Constants.XML_CONSTRAINT_MENTHOD);
				// add these clones to selectedAntibodies
				this.operation.insertAllAntibodiesInOrder(selectedAntibodies, randomAntibodies, antibodyNumber, this.riskFactor.getSbp(),paretoOptimalMethod);
				selectedAntibodies = this.operation.select(selectedAntibodies, antibodyNumber);
				
				if (bestAntibody == null){
					bestAntibody = selectedAntibodies.get(0);
				} else if (this.operation.individualSharedFitness(bestAntibody, antibodyNumber, this.riskFactor.getSbp(), paretoOptimalMethod)
						 < this.operation.individualSharedFitness(selectedAntibodies.get(0), antibodyNumber, this.riskFactor.getSbp(), paretoOptimalMethod)){
					bestAntibody = selectedAntibodies.get(0);
				}
			}
			bestAntibodies.add(bestAntibody);
		}
		return bestAntibodies;
	}
	
	public Map<String, Double> getDifferenceFromPreprocessing(){

		return difference;
	}
	
	public int getAntibodyNumber() {
		return antibodyNumber;
	}

	public void setAntibodyNumber(int antibodyNumber) {
		this.antibodyNumber = antibodyNumber;
	}

	public int getCloneIteration() {
		return cloneIteration;
	}

	public void setCloneIteration(int cloneIteration) {
		this.cloneIteration = cloneIteration;
	}

	public int getParetoOptimalMethod() {
		return paretoOptimalMethod;
	}

	public void setParetoOptimalMethod(int paretoOptimalMethod) {
		this.paretoOptimalMethod = paretoOptimalMethod;
	}

	public int getBetaValue() {
		return betaValue;
	}

	public void setBetaValue(int betaValue) {
		this.betaValue = betaValue;
	}

	public int getClonedAntibodyNr() {
		return clonedAntibodyNr;
	}

	public void setClonedAntibodyNr(int clonedAntibodyNr) {
		this.clonedAntibodyNr = clonedAntibodyNr;
	}
}
